﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using SymulatorEthernetWPF.Shared;
using SymulatorEthernetWPF.SimObjects.Devices;
using SymulatorEthernetWPF.UserInterface;
using SymulatorEthernetWPF.SimObjects.Layers.Physical;
using System.Collections.ObjectModel;

namespace SymulatorEthernetWPF.SimObjects.Wires
{
    [Serializable]
    public class Wire : Shape, ISerializable
    {
        private readonly ContextMenu _contextMenu = new ContextMenu();
        public Guid GUID;
        private bool _isEnabled;

        private WireEnd _wireEndA;
        private WireEnd _wireEndB;

        public bool isDuplex = false;


        public WireEnd wireEndA
        {
            get
            {
                return _wireEndA;
            }
            set
            {
                _wireEndA = value;
                try
                {
                    if (_wireEndA.networkInterface.Owner is SwitchControl || _wireEndB.networkInterface.Owner is SwitchControl || (_wireEndA.networkInterface.Owner is HostControl && _wireEndB.networkInterface.Owner is HostControl))
                        isDuplex = true;
                    else
                        isDuplex = false;
                }
                catch
                {
                    isDuplex=false;
                }
            }
        }
        public WireEnd wireEndB
        {
            get
            {
                return _wireEndB;
            }
            set
            {
                _wireEndB = value;
                try
                {
                    if (_wireEndA.networkInterface.Owner is SwitchControl || _wireEndB.networkInterface.Owner is SwitchControl || (_wireEndA.networkInterface.Owner is HostControl && _wireEndB.networkInterface.Owner is HostControl))
                        isDuplex = true;
                    else
                        isDuplex = false;
                }
                catch
                {
                    isDuplex=false;
                }
            }
        }


        public List<WireBenderHandler> WireBenderHandlerList = new List<WireBenderHandler>();
        public ConnectionType connectionType = ConnectionType.Ethernet;

        public ObservableCollection<Signal> signals = new ObservableCollection<Signal>();

        public double Length
        {
            get
            {
                if (WireBenderHandlerList.Count != 0)
                {
                    double tmp=0;
                    tmp = StaticFunctions.GetLength(wireEndA.networkInterface.Position, WireBenderHandlerList[0].Position);
                    for (int i = 1; i < WireBenderHandlerList.Count - 2; i++)
                    {
                        tmp += StaticFunctions.GetLength(WireBenderHandlerList[i].Position, WireBenderHandlerList[i+1].Position);
                    }
                    tmp += StaticFunctions.GetLength(WireBenderHandlerList[WireBenderHandlerList.Count - 1].Position, wireEndB.networkInterface.Position);
                    return tmp;
                }
                else
                {
                    return StaticFunctions.GetLength(wireEndA.networkInterface.Position, wireEndB.networkInterface.Position);
                }
            }
        }


        public void addMouseEvent()
        {
            StaticVars.MainWindow.scrollViewer.MouseMove += new MouseEventHandler(updateWire);
        }


        public void removeMouseEvent()
        {
            StaticVars.MainWindow.scrollViewer.MouseMove -= new MouseEventHandler(updateWire);
        }

        #region Shape

        public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof (double),typeof (Wire),new FrameworkPropertyMetadata(0.0,FrameworkPropertyMetadataOptions.AffectsRender |FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof (double),typeof (Wire),new FrameworkPropertyMetadata(0.0,FrameworkPropertyMetadataOptions.AffectsRender |FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof (double),typeof (Wire),new FrameworkPropertyMetadata(0.0,FrameworkPropertyMetadataOptions.AffectsRender |FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof (double),typeof (Wire),new FrameworkPropertyMetadata(0.0,FrameworkPropertyMetadataOptions.AffectsRender |FrameworkPropertyMetadataOptions.AffectsMeasure));

        [TypeConverter(typeof (LengthConverter))]
        public double X1
        {
            get { return (double) GetValue(X1Property); }
            set { SetValue(X1Property, value); }
        }

        [TypeConverter(typeof (LengthConverter))]
        public double Y1
        {
            get { return (double) GetValue(Y1Property); }
            set { SetValue(Y1Property, value); }
        }

        [TypeConverter(typeof (LengthConverter))]
        public double X2
        {
            get { return (double) GetValue(X2Property); }
            set { SetValue(X2Property, value); }
        }

        [TypeConverter(typeof (LengthConverter))]
        public double Y2
        {
            get { return (double) GetValue(Y2Property); }
            set { SetValue(Y2Property, value); }
        }

        protected override Geometry DefiningGeometry
        {
            get
            {
                var geometry = new StreamGeometry {FillRule = FillRule.EvenOdd};

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawLine(context);
                }

                geometry.Freeze();

                return geometry;
            }
        }

        private void InternalDrawLine(StreamGeometryContext context)
        {
            var pt1 = new Point(X1, Y1);
            var pt2 = new Point(X2, Y2);

            context.BeginFigure(pt1, true, false);
            foreach (WireBenderHandler it in WireBenderHandlerList)
            {
                context.LineTo(it.Position, true, true);
            }
            context.LineTo(pt2, true, true);
        }

        #endregion Shape

        public Wire(SerializationInfo info, StreamingContext context)
        {
            StaticVars.IsSettingUpWire = false;
            GUID = (Guid) info.GetValue("GUID", typeof (Guid));
            X1 = (double) info.GetValue("X1", typeof (double));
            X2 = (double) info.GetValue("X2", typeof (double));
            Y1 = (double) info.GetValue("Y1", typeof (double));
            Y2 = (double) info.GetValue("Y2", typeof (double));
            wireEndA = (WireEnd) info.GetValue("wireEndA", typeof (WireEnd));
            wireEndB = (WireEnd)info.GetValue("wireEndB", typeof(WireEnd));
            try
            {
                connectionType = (ConnectionType)info.GetValue("connectionType", typeof(ConnectionType));
            }
            catch
            {
                connectionType = ConnectionType.Ethernet;
            }
            WireBenderHandlerList =
                (List<WireBenderHandler>) info.GetValue("wireBenderList", typeof (List<WireBenderHandler>));
            _isEnabled = (bool) info.GetValue("isEnabled", typeof (bool));

            HorizontalAlignment = HorizontalAlignment.Left;
            VerticalAlignment = VerticalAlignment.Top;
            StrokeThickness = 3;

            var deleteMenuItem = new MenuItem {Header = "Usuń"};
            deleteMenuItem.Click += DeleteMenuItemClick;

            
            _contextMenu.Items.Add(deleteMenuItem);
            ContextMenu = _contextMenu;
            MouseLeftButtonDown += WireMouseDown;
            signals.CollectionChanged += signals_CollectionChanged;
        }           

        public Wire(NetworkInterface networkInterfaceA)
        {
            GUID = Guid.NewGuid();
            wireEndA = new WireEnd(this, networkInterfaceA);
            X1 = 0;
            Y1 = 0;
            X2 = 0;
            Y2 = 0;
            Stroke = Brushes.Gray;
            HorizontalAlignment = HorizontalAlignment.Left;
            VerticalAlignment = VerticalAlignment.Top;
            StrokeThickness = 3;
            var deleteMenuItem = new MenuItem {Header = "Usuń"};
            deleteMenuItem.Click += DeleteMenuItemClick;



            _contextMenu.Items.Add(deleteMenuItem);
            ContextMenu = _contextMenu;
            MouseLeftButtonDown += WireMouseDown;
            addMouseEvent();
            signals.CollectionChanged += signals_CollectionChanged;
        }


        public void updateWire(object sender=null, MouseEventArgs e=null)
        {
            if (wireEndB == null)
            {
                Stroke = Brushes.Gray;
                X1 = wireEndA.networkInterface.Position.X;
                Y1 = wireEndA.networkInterface.Position.Y;
                X2 = Mouse.GetPosition(StaticVars.MainWindow.customCanvas).X;
                Y2 = Mouse.GetPosition(StaticVars.MainWindow.customCanvas).Y;
                if (X2 < X1 && Y2 < Y1) //LG
                {
                    X2 += 2;
                    Y2 += 2;
                }
                if (X2 < X1 && Y2 > Y1) //LD
                {
                    X2 += 2;
                    Y2 -= 2;
                }
                if (X2 > X1 && Y2 < Y1) //PG
                {
                    X2 -= 2;
                    Y2 += 2;
                }
                if (X2 > X1 && Y2 > Y1) //PD
                {
                    X2 -= 2;
                    Y2 -= 2;
                }
            }
            else if (wireEndA == null)
            {
                Stroke = Brushes.Gray;
                X2 = wireEndB.networkInterface.Position.X;
                Y2 = wireEndB.networkInterface.Position.Y;
                X1 = Mouse.GetPosition(StaticVars.MainWindow.customCanvas).X;
                Y1 = Mouse.GetPosition(StaticVars.MainWindow.customCanvas).Y;
                if (X1 < X2 && Y1 < Y2) //LG
                {
                    X1 += 2;
                    Y1 += 2;
                }
                if (X1 < X2 && Y1 > Y2) //LD
                {
                    X1 += 2;
                    Y1 -= 2;
                }
                if (X1 > X2 && Y1 < Y2) //PG
                {
                    X1 -= 2;
                    Y1 += 2;
                }
                if (X1 > X2 && Y1 > Y2) //PD
                {
                    X1 -= 2;
                    Y1 -= 2;
                }
            }
            else
            {
                try
                {
                    X1 = wireEndA.networkInterface.Position.X;
                    Y1 = wireEndA.networkInterface.Position.Y;
                    X2 = wireEndB.networkInterface.Position.X;
                    Y2 = wireEndB.networkInterface.Position.Y;
                    if (wireEndA.networkInterface.IsEnabled && wireEndB.networkInterface.IsEnabled)
                    {
                        Stroke = Brushes.Black;
                    }
                    else
                    {
                        Stroke = Brushes.Gray;
                    }
                }
                catch { }
            }
        }

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("GUID", GUID);
            info.AddValue("X1", X1);
            info.AddValue("X2", X2);
            info.AddValue("Y1", Y1);
            info.AddValue("Y2", Y2);
            info.AddValue("wireEndA", wireEndA);
            info.AddValue("wireEndB", wireEndB);
            info.AddValue("isEnabled", _isEnabled);
            info.AddValue("wireBenderList", WireBenderHandlerList);
            info.AddValue("connectionType", connectionType);
        }

        #endregion

        private void WireMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!StaticVars.IsSettingUpWire)
            {
                var tmpwireBenderHandler = new WireBenderHandler(this,
                                                                 Mouse.GetPosition(StaticVars.MainWindow.customCanvas).X,
                                                                 Mouse.GetPosition(StaticVars.MainWindow.customCanvas).Y);
                StaticVars.MainWindow.customCanvas.Children.Add(tmpwireBenderHandler);
                WireBenderHandlerList = StaticFunctions.SortWireBenderPoints(WireBenderHandlerList, wireEndA.networkInterface,
                                                                             wireEndB.networkInterface, tmpwireBenderHandler);
                X1++;
                X1--;
                
            }
        }

        private void DeleteMenuItemClick(object sender, RoutedEventArgs e)
        {
            if (MessageBox.Show("Usunąć połączenie?",  "Potwierdzenie", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                DeleteWire();
            }
            
        }

        public void DeleteWire()
        {
            Simulation.UndoStack.Push(new HistoryElement());
            Simulation.RedoStack.Clear();
            if (wireEndA != null)
                if (wireEndA.networkInterface != null)
                    wireEndA.networkInterface.wireEnd = null;
            if(wireEndB!=null)
                if (wireEndB.networkInterface != null)
                    wireEndB.networkInterface.wireEnd = null;
            wireEndA = null;
            wireEndB = null;
            StaticVars.MainWindow.customCanvas.Children.Remove(this);
            foreach (WireBenderHandler it in WireBenderHandlerList)
            {
                StaticVars.MainWindow.customCanvas.Children.Remove(it);
            }
            WireBenderHandlerList = null;
        }


        public bool IsUsable()
        {
            if (wireEndA.networkInterface.IsEnabled && wireEndB.networkInterface.IsEnabled)
                return true;
            else return false;
        }

        void signals_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (signals.Count == 0)
            {
                this.Stroke = Brushes.Black;
                StrokeThickness = 3;
                return;
            }
            foreach (Signal it in signals)
            {
                if (it.isJam)
                {
                    this.Stroke = Brushes.Red;
                    StrokeThickness = 5;
                    return;
                }
            }
            this.Stroke = Brushes.Blue;
            StrokeThickness = 5;
        }
    }
}